package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.bean.BeanUtils;
import com.zzyl.framework.config.properties.HuaWeiIotConfigProperties;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceDataService;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.IotMsgNotifyData;
import com.zzyl.nursing.vo.IotMsgService;
import com.zzyl.nursing.vo.ProjectVo;
import com.zzyl.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备Service业务层处理
 * 
 * @author zzyl
 * @date 2025-10-23
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 查询设备
     * 
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询设备列表
     * 
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return save(device) ? 1 : 0;
    }

    /**
     * 修改设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device)
    {
        return updateById(device) ? 1 : 0;
    }

    /**
     * 批量删除设备
     * 
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除设备信息
     * 
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    @Autowired
    private IoTDAClient ioTDAClient;
    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 从物联网平台同步产品列表
     */
    @Override
    public void syncProductList() {

        ListProductsResponse response = ioTDAClient.listProducts(new ListProductsRequest());
        if (response.getHttpStatusCode() != HttpStatus.HTTP_OK) {
            throw new BaseException("同步产品列表失败");
        }

        List<ProductSummary> productSummaryList = response.getProducts();
        String jsonStr = JSONUtil.toJsonStr(productSummaryList);

        if (CollUtil.isNotEmpty(productSummaryList)) {
            redisTemplate.opsForValue().set(CacheConstants.IOT_PROJECT_LIST_KEY, jsonStr);
        }
    }

    /**
     * 查询所有产品列表
     *
     * @return
     */
    @Override
    public List<ProjectVo> allProduct() {

        String ProjectJosnStr = redisTemplate.opsForValue().get(CacheConstants.IOT_PROJECT_LIST_KEY);
        if (StrUtil.isBlank(ProjectJosnStr)) {
            return CollUtil.newArrayList();
        }

        List<ProductSummary> productSummaryList = JSONUtil.toList(ProjectJosnStr, ProductSummary.class);

        List<ProjectVo> projectVoList = productSummaryList.stream()
                .map(project -> ProjectVo.builder().productKey(project.getProductId()).productName(project.getName()).build())
                .collect(Collectors.toList());

        return projectVoList;
    }

    /**
     * 注册设备
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(DeviceDto dto) {
        // 1、判断设备名称是否重复
        Long countByDeviceName = this.lambdaQuery().eq(Device::getDeviceName, dto.getDeviceName()).count();
        if (countByDeviceName > 0) {
            throw new BaseException("设备名称重复");
        }

        // 2、设置设备标识 - 雪花算法
        String nodeId = IdUtil.getSnowflakeNextIdStr();

        // 3、判断同一个位置上是否绑定了相同的产品
        Long countByLocation = this.lambdaQuery().eq(Device::getBindingLocation, dto.getBindingLocation())
                .eq(Device::getLocationType, dto.getLocationType())
                .eq(Device::getProductKey, dto.getProductKey()).count();
        if (countByLocation > 0) {
            throw new BaseException("同一个位置上不能绑定相同的产品");
        }

        try {
            // 4、注册设备到华为iot平台
            AddDeviceRequest addDeviceRequest = new AddDeviceRequest();

            AddDevice addDevice = new AddDevice();
            addDevice.setNodeId(nodeId);
            addDevice.setDescription(dto.getNickname());
            addDevice.setDeviceName(dto.getDeviceName());
            addDevice.setProductId(dto.getProductKey());

            AuthInfo authInfo = new AuthInfo();
            String secret = IdUtil.fastSimpleUUID();
            authInfo.setSecret(secret);

            addDevice.setAuthInfo(authInfo);

            addDeviceRequest.setBody(addDevice);

            AddDeviceResponse addDeviceResponse = ioTDAClient.addDevice(addDeviceRequest);
            if (addDeviceResponse.getHttpStatusCode() != HttpStatus.HTTP_CREATED) {
                throw new BaseException("注册设备失败");
            }

            // 保存到数据库中
            Device device = BeanUtil.toBean(dto, Device.class);
            device.setIotId(addDeviceResponse.getDeviceId());//设备ID由物联网平台分配获得，生成规则为product_id + _ + node_id拼接而成
            device.setNodeId(nodeId);
            device.setSecret(secret);

            String productName = getProductNameFromCache(dto.getProductKey());
            device.setProductName(productName);
            device.setHaveEntranceGuard(dto.getNickname().contains("门禁") ? 1 : 0);

            this.save(device);

        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("注册设备失败");
        }
    }

    @Autowired
    private ISysUserService userService;
    @Autowired
    private HuaWeiIotConfigProperties iotConfigProperties;

    /**
     * 查询设备详细数据
     *
     * @param dto
     */
    @Override
    public DeviceDetailVo queryDeviceDetail(DeviceDto dto) {

        // 根据设备id从表中查询
        Device device = this.lambdaQuery().eq(Device::getIotId, dto.getIotId()).one();
        if (ObjectUtil.isNull(device)) {
            throw new BaseException("该设备不存在");
        }

        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        deviceDetailVo.setDeviceSecret(device.getSecret());
        deviceDetailVo.setCreator(userService.selectUserById(Long.valueOf(device.getCreateBy())).getUserName());
        deviceDetailVo.setGmtCreate(device.getCreateTime());
        deviceDetailVo.setRegion(iotConfigProperties.getRegionId());

        // 调用华为云iot平台接口查询设备
        ShowDeviceRequest showDeviceRequest = new ShowDeviceRequest();
        showDeviceRequest.withDeviceId(dto.getIotId());
        try {
            ShowDeviceResponse showDeviceResponse = ioTDAClient.showDevice(showDeviceRequest);
            if (showDeviceResponse.getHttpStatusCode() != HttpStatus.HTTP_OK) {
                throw new BaseException("调用华为IO接口查询设备详情失败");
            }

            BeanUtils.copyBeanProp(deviceDetailVo, showDeviceResponse);
            deviceDetailVo.setDeviceStatus(showDeviceResponse.getStatus());
            String activeTime = showDeviceResponse.getActiveTime();
            if (StrUtil.isNotBlank(activeTime)) {
                LocalDateTime gmtActive = OffsetDateTime.parse(activeTime)
                        .atZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                        .toLocalDateTime();
                deviceDetailVo.setGmtActive(gmtActive);
            }
            if (StrUtil.isNotBlank(showDeviceResponse.getFwVersion())) {
                deviceDetailVo.setFirmwareVersion(showDeviceResponse.getFwVersion());
            }
            return deviceDetailVo;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("调用华为IO接口查询设备详情失败");
        }
    }

    /**
     * 查看指定产品的已发布物模型中的功能定义详情
     *
     * @param productKey
     */
    @Override
    public JSONObject queryProductModelInfo(String productKey) {
        if (StrUtil.isBlank(productKey)) {
            throw new BaseException("该产品id不存在");
        }

        // 从物联网上获取物模型中的功能定义详情
        ShowProductRequest showProductRequest = new ShowProductRequest();
        showProductRequest.setProductId(productKey);
        ShowProductResponse showProductResponse;
        try {
            showProductResponse = ioTDAClient.showProduct(showProductRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("获取失败");
        }

        List<ServiceCapability> serviceCapabilities = showProductResponse.getServiceCapabilities();
        if (CollUtil.isNotEmpty(serviceCapabilities)) {
            JSONObject jsonObject = JSONUtil.parseObj(serviceCapabilities.get(0));
            return jsonObject;
        }

        return null;
    }

    /**
     * 查询指定设备的物模型运行状态
     *
     * @param deviceName
     * @param productKey
     */
    @Override
    public Map<String, Object> queryDevicePropertyStatus(String deviceName, String productKey) {
        // 根据产品id和设备名查询设备id
        Device device = this.lambdaQuery().eq(Device::getDeviceName, deviceName)
                .eq(Device::getProductKey, productKey).one();
        if (device == null) {
            throw new BaseException("设备不存在");
        }

        // 从iot平台获取数据
        ShowDeviceShadowRequest showDeviceShadowRequest = new ShowDeviceShadowRequest();
        showDeviceShadowRequest.withDeviceId(device.getIotId());
        try {
            ShowDeviceShadowResponse showDeviceShadowResponse = ioTDAClient.showDeviceShadow(showDeviceShadowRequest);
            if (showDeviceShadowResponse.getHttpStatusCode() != HttpStatus.HTTP_OK) {
                throw new BaseException("读取失败");
            }
            List<DeviceShadowData> shadow = showDeviceShadowResponse.getShadow();
            if (CollUtil.isEmpty(shadow)) {
                return Collections.emptyMap();
            }
            DeviceShadowData deviceShadowData = shadow.get(0);

            if (ObjectUtil.isNotEmpty(deviceShadowData)) {
                String eventTime = deviceShadowData.getReported().getEventTime();
                LocalDateTime reportTime = OffsetDateTime.parse(eventTime, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX"))
                        .atZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                        .toLocalDateTime();
                JSONObject jsonObject = JSONUtil.parseObj(deviceShadowData.getReported().getProperties());

                List<Map<String, Object>> list = new ArrayList<>();

                if (ObjectUtil.isNotEmpty(jsonObject)) {
                    jsonObject.forEach((k, v) -> {
                        Map<String, Object> map = new HashMap<>();
                        map.put("identifier", k);
                        map.put("value", v);
                        map.put("time", reportTime);
                        list.add(map);
                    });

                    Map<String, Object> propertyStatusInfo = new HashMap<>();
                    propertyStatusInfo.put("propertyStatusInfo", list);

                    Map<String, Object> resultMap = new HashMap<>();
                    resultMap.put("list", propertyStatusInfo);
                    return resultMap;
                }
            }
            return Collections.emptyMap();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("读取失败");
        }
    }

    /**
     * 修改设备备注名称
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateDeviceNickname(DeviceDto dto) {
        Device device = this.lambdaQuery().eq(Device::getIotId, dto.getIotId()).one();
        if (ObjectUtil.isEmpty(device)) {
            throw new BaseException("该设备不存在");
        }

        // 数据库修改
        this.lambdaUpdate().eq(Device::getIotId, dto.getIotId())
                .set(StrUtil.isNotBlank(dto.getNickname()), Device::getNickname, dto.getNickname())
                .set(StrUtil.isNotBlank(dto.getDeviceDescription()), Device::getDeviceDescription, dto.getDeviceDescription())
                .set(dto.getLocationType() != null, Device::getLocationType, dto.getLocationType())
                .set(StrUtil.isNotBlank(dto.getRemark()), Device::getRemark, dto.getRemark())
                .update();

        // 华为iot平台修改设备信息
        UpdateDeviceRequest updateDeviceRequest = new UpdateDeviceRequest();
        updateDeviceRequest.withDeviceId(device.getIotId());
        UpdateDevice updateDevice = new UpdateDevice();
        updateDevice.setDeviceName(dto.getDeviceName());
        updateDevice.setDescription(dto.getNickname());

        updateDeviceRequest.setBody(updateDevice);
        try {
            UpdateDeviceResponse updateDeviceResponse = ioTDAClient.updateDevice(updateDeviceRequest);
            if (updateDeviceResponse.getHttpStatusCode() != HttpStatus.HTTP_OK) {
                throw new BaseException("修改设备失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("修改设备失败");
        }
    }

    /**
     * 删除设备
     *
     * @param iotId
     * @param productKey
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteDevice(String iotId, String productKey) {
        Device device = this.lambdaQuery().eq(Device::getIotId, iotId)
                .eq(Device::getProductKey, productKey).one();
        if (ObjectUtil.isEmpty(device)) {
            throw new BaseException("设备不存在");
        }

        // 删除数据库的对应设备数据
        this.remove(Wrappers.<Device>lambdaQuery().eq(Device::getIotId, iotId));

        // 删除iot平台的设备数据
        DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
        deleteDeviceRequest.withDeviceId(iotId);
        try {
            DeleteDeviceResponse deleteDeviceResponse = ioTDAClient.deleteDevice(deleteDeviceRequest);
            if (deleteDeviceResponse.getHttpStatusCode() != HttpStatus.HTTP_NO_CONTENT) {
                throw new BaseException("设备删除失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("设备删除失败");
        }
    }

    @Autowired
    private IDeviceDataService deviceDataService;

    /**
     * 获取上报的消息保存到设备数据表
     *
     * @param notifyData
     */
    @Override
    public void handleMessageData(IotMsgNotifyData notifyData) {

        // 1、获取设备id
        String deviceId = notifyData.getHeader().getDeviceId();

        // 2、判断设备是否存在
        Device device = this.lambdaQuery().eq(Device::getIotId, deviceId).one();
        if (device == null) {
            throw new BaseException("设备不存在");
        }

        // 3、获取上报的服务信息
        List<IotMsgService> services = notifyData.getBody().getServices();
        services.forEach(x -> {
            Map<String, Object> properties = x.getProperties();
            String eventTime = x.getEventTime();
            LocalDateTime alarmTime = OffsetDateTime.parse(eventTime, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX"))
                    .atZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();

            List<DeviceData> deviceDataList = properties.entrySet().stream().map(entry -> {
                String functionId = entry.getKey();
                Object dataValue = entry.getValue();
                return DeviceData.builder()
                        .deviceName(device.getDeviceName()) // 设备名
                        .iotId(deviceId) // 设备id
                        .nodeId(device.getNodeId())  // 节点id
                        .nickname(device.getNickname()) // 设备昵称
                        .productKey(device.getProductKey()) // 产品key
                        .productName(device.getProductName()) // 产品名称
                        .functionId(functionId)
                        .accessLocation(device.getBindingLocation()) // 绑定位置
                        .locationType(device.getLocationType()) // 未知类型
                        .physicalLocationType(device.getPhysicalLocationType()) // 物理位置
                        .dataValue(String.valueOf(dataValue))
                        .alarmTime(alarmTime) // 上报时间
                        .build();
            }).collect(Collectors.toList());

//            List<DeviceData> deviceDataList = new ArrayList<>();
//
//            // 4、获取上报的物模型数据信息
//            properties.forEach((k, v) -> {
//                String functionId = k; // 功能名（设备的物模型属性名）
//                Object dataValue = v; // 属性上报的值
//
//                DeviceData deviceData = DeviceData.builder()
//                        .deviceName(device.getDeviceName()) // 设备名
//                        .iotId(deviceId) // 设备id
//                        .nodeId(device.getNodeId())  // 节点id
//                        .nickname(device.getNickname()) // 设备昵称
//                        .productKey(device.getProductKey()) // 产品key
//                        .productName(device.getProductName()) // 产品名称
//                        .functionId(functionId)
//                        .accessLocation(device.getBindingLocation()) // 绑定位置
//                        .locationType(device.getLocationType()) // 未知类型
//                        .physicalLocationType(device.getPhysicalLocationType()) // 物理位置
//                        .dataValue(String.valueOf(dataValue))
//                        .alarmTime(alarmTime) // 上报时间
//                        .build();
//                deviceDataList.add(deviceData);
//            });
            // 5、批量新增上报消息
            deviceDataService.saveBatch(deviceDataList);

            // 6、将最近一次上报的数据保存到redis中
            redisTemplate.opsForHash().put(CacheConstants.IOT_DEVICE_DATA_LATEST, deviceId, JSONUtil.toJsonStr(deviceDataList));
        });
    }

    /**
     * 从缓存中根据产品ID获取产品名
     * @param productKey
     * @return
     */
    private String getProductNameFromCache(String productKey) {
        String JsonStr = redisTemplate.opsForValue().get(CacheConstants.IOT_PROJECT_LIST_KEY);
        if (StrUtil.isBlank(JsonStr)) {
            return null;
        }

        List<ProductSummary> productSummaryList = JSONUtil.toList(JsonStr, ProductSummary.class);
        ProductSummary productSummary = productSummaryList.stream().filter(x -> productKey.equals(x.getProductId())).findFirst().get();
        return productSummary.getName();
    }


}
